import os.path
import sqlite3
from collections import defaultdict

from tags import tag2setname, setcode, all_tt, banned, no_limit

SINGLE_TAG_MIN = 5
SUM_TAG_MIN = 20


class CardChecker:
    def __init__(self, allowed_tag):
        self.allowed_tag = allowed_tag
        self.cache = {k: [] for k in self.allowed_tag}
        self.not_in_card = []
        self.errs = []
        self._get_code()
        print(os.path.curdir)
        self.card_db_conn = sqlite3.connect('./res/cards.cdb')  # 替换 'your_database.db' 为你的SQLite数据库文件路径
        self.test_release_conn = sqlite3.connect('./res/test-release.cdb')  # 替换 'your_database.db' 为你的SQLite数据库文件路径
        self.test_update_conn = sqlite3.connect('./res/test-update.cdb')  # 替换 'your_database.db' 为你的SQLite数据库文件路径
        self.ygo_count = defaultdict(lambda:0)
        self.visited = []
        self.card_name = defaultdict(lambda: 0)

    def _get_code(self):
        self.code_mapping = defaultdict(list)
        self.deck_name_mapping = defaultdict(list)
        self.all_deck_names = []
        for t in self.allowed_tag:
            rs = tag2setname[t]
            self.all_deck_names.extend(rs)
            for r in rs:
                self.code_mapping[t].append(setcode[r])

    def clear_card_name(self):
        self.card_name = defaultdict(lambda: 0)

    def rel(self):
        self.card_db_conn.close()
        self.test_update_conn.close()
        self.test_update_conn.close()


    def card_name_query(self, id, cursor):
        # 定义要查询的ID
        id_to_filter = id  # 替换为你要查询的ID

        # 执行SQL查询
        cursor.execute('SELECT name FROM texts WHERE id = ?', (id_to_filter,))
        result = cursor.fetchone()

        # 处理结果
        if result:
            return True, result[0]
        else:
            return False, []

    def card_text_query(self, id, cursor):
        # 定义要查询的ID
        id_to_filter = id  # 替换为你要查询的ID

        # 执行SQL查询
        cursor.execute('SELECT desc FROM texts WHERE id = ?', (id_to_filter,))
        result = cursor.fetchone()

        # 处理结果
        if result:
            return True, result[0]
        else:
            return False, []

    def card_set_query(self, id, cursor):
        # 定义要查询的ID
        id_to_filter = id  # 替换为你要查询的ID

        # 执行SQL查询
        cursor.execute('SELECT setcode FROM datas WHERE id = ?', (id_to_filter,))
        result = cursor.fetchone()

        # 处理结果
        if result:
            setcode = result[0]
            # 将十进制数转换为二进制字符串
            binary_string = bin(setcode)[2:]

            # 确保结果是16的倍数位数，如果不是，在开头补0
            while len(binary_string) % 16 != 0:
                binary_string = '0' + binary_string

            # 将二进制字符串拆分为16位一组
            binary_groups = [binary_string[i:i + 16] for i in range(0, len(binary_string), 16)]

            # 将每组二进制数转换为十六进制
            hexadecimal_result = [hex(int(group, 2)) for group in binary_groups]

            if len(hexadecimal_result) == 1 and hexadecimal_result[0] == '0x0':
                return True, []
            return True, hexadecimal_result
        else:
            return False, []



    def check(self, main, check_type='all'):
        self.not_in_card = []
        self.errs = []

        counter = {k: 0 for k in self.allowed_tag}

        def search_code(c,codes):
            if len(codes) <= 0:
                return True
            for k, v in self.code_mapping.items():
                for r in v:
                    if r in codes:
                        counter[k] += 1
                        self.cache[k].append(c)
                        return True
            for no_l in no_limit.values():
                if no_l in codes:
                    return True
            return False

        def search_text(c,text):
            for t in self.allowed_tag:
                rs = tag2setname[t]
                for r in rs:
                    print(r,text)
                    if r in text:
                        counter[t] += 1
                        self.cache[t].append(c)
                        return True
            print("全部没有命中")
            return False

        def check_single(c,num):
            if num > 3:
                self.errs.append(f"卡片{str(c)}没有通过卡检：超出最大投入上限。")
                return False
            if str(c) in banned:
                if banned[str(c)] < num:
                    self.errs.append(f"卡片{str(c)}没有通过卡检：超出使用的卡表限制。")
                    return False
            return True

        # 创建游标对象
        cursor1 = self.card_db_conn.cursor()
        cursor2 = self.test_release_conn.cursor()
        cursor3 = self.test_update_conn.cursor()
        cursors = [cursor1, cursor2, cursor3]
        if check_type == 'info':
            for c in main:
                find_one = False
                for cr in cursors:
                    res,name = self.card_name_query(c,cr)
                    if res is True:
                        find_one = True
                        self.card_name[name] += 1
                        break
                if not find_one:
                    self.errs.append(f"卡片{str(c)}没有通过卡检：服务器不存在该卡。")
                    return False

        if check_type == 'type' or check_type == 'all':
            for c in main:

                self.ygo_count[c] +=1
                if not check_single(c,self.ygo_count[c]):
                    return False
                if c in self.visited:
                    continue
                find_one = False
                codes = []
                for cr in cursors:
                    res, codes = self.card_set_query(c, cr)
                    if res is False:
                        continue
                    else:
                        find_one = True
                        break
                if find_one is False:
                    self.errs.append(f"卡片{str(c)}没有通过卡检：服务器不存在该卡。")
                    return False

                res = False

                if len(codes)>0:
                    res = search_code(c,codes)

                print(c,codes)

                if res is True:
                    continue
                    # 是允许字段:
                else:
                    print("没有字段或者不允许字段",c)

                    find_one = False
                    text = ""
                    for cr in cursors:
                        res, text = self.card_text_query(c, cr)
                        if res is False:
                            continue
                        else:
                            find_one = True
                            break
                    if find_one is False:
                        self.errs.append(f"卡片{str(c)}没有通过卡检：服务器不存在该卡。")
                        return False
                    r2 = search_text(c,text)
                    if not r2:
                        if len(codes)>0:
                            self.errs.append(f"卡片{str(c)}没有通过卡检：不属于{str(','.join(self.allowed_tag))}系列的任何一个字段或者没有对应的字段记述，具体的可用字段包括:{str(','.join(self.all_deck_names))}")
                            return False
                        else:
                            continue
                # self.visited.append(c)

        if check_type == 'count' or check_type == 'all':
            # 最后检查数量
            sums = 0
            print(counter)
            for t, n in counter.items():
                sums += n
                if n < SINGLE_TAG_MIN:
                    self.errs.append(f"卡组没有通过卡检：{str(t)}字段卡牌数量少于{str(SINGLE_TAG_MIN)}张。")
                    return False
            if sums < SUM_TAG_MIN:
                self.errs.append(
                        f"卡组没有通过卡检：{str(','.join(self.allowed_tag))}字段的卡牌总数少于{str(SUM_TAG_MIN)}张。")
                return False

        # 关闭游标和连接
        for cr in cursors:
            cr.close()
        return True


def is_integer(string):
    # 首先去除字符串两端的空白字符
    string = string.strip()

    # 如果字符串为空，返回False
    if not string:
        return False

    # 如果字符串第一个字符是+或-，则去除它
    if string[0] in ('+', '-'):
        string = string[1:]

    # 检查剩余的字符是否都是数字
    return string.isdigit()

def check_deck_file(deck):
    main = []
    extra = []
    side = []

    sta = 0

    with open(deck, 'r') as file:
        for line in file:
            line = line.strip()
            if "#main" in line:
                sta = 0
                continue
            if "#extra" in line:
                sta = 1
                continue

            if line.startswith("#"):
                continue

            if "!side" in line:
                sta = 2
                continue

            if not is_integer(line):
                continue

            if sta == 0:
                main.append(line)
            elif sta == 1:
                extra.append(line)
            else:
                side.append(line)
    return main,extra,side

def check_deck_lines(deck):
    main = []
    extra = []
    side = []

    sta = 0

    lines = deck.split('\n')

    for line in lines:
        line = line.strip()
        if "#main" in line:
            sta = 0
            continue
        if "#extra" in line:
            sta = 1
            continue

        if line.startswith("#"):
            continue

        if "!side" in line:
            sta = 2
            continue

        if not is_integer(line):
            continue

        if sta == 0:
            main.append(line)
        elif sta == 1:
            extra.append(line)
        else:
            side.append(line)

    return main,extra,side

def check_deck(origin_main, extra, side, tag_list=None, count=True, tag_or_id='tag'):
    if tag_list is None:
        tag_list = []
    if count:
        if len(origin_main) <40 or len(origin_main)>60:
            return False, f"主卡组卡检失败！卡片数量{str(len(origin_main))}不符合要求。"
        if len(extra) >15:
            return False, f"额外卡组卡检失败！卡片数量{str(len(origin_main))}不符合要求。"
        if len(side) >15:
            return False, f"side卡组卡检失败！卡片数量{str(len(origin_main))}不符合要求。"

    main = origin_main + extra

    if tag_or_id == 'tag':
        for tag in tag_list:
            if tag not in all_tt:
                return False, f"{tag}字段不存在"

    cc = CardChecker(tag_list)
    if tag_or_id == 'tag':
        main_res = cc.check(main, 'all')
        if not main_res:
            cc.rel()
            return False,f"主卡组/额外卡组卡检失败！{'/'.join(cc.errs)}"
        side_res = cc.check(side, 'type')
        if not side_res:
            cc.rel()
            return False,f"副卡组卡检失败！{'/'.join(cc.errs)}"
        cc.rel()
        return True,cc.cache
    else:
        cc.check(origin_main, 'info')
        main_info = cc.card_name
        cc.clear_card_name()

        cc.check(extra, 'info')
        extra_info = cc.card_name
        cc.clear_card_name()

        cc.check(side, 'info')
        side_info = cc.card_name
        cc.rel()

        return True,{"main":main_info,"extra":extra_info,"side":side_info}